/**
*		@file GenerateOptimizedSimple.cpp
*		@brief generate the stall optimized simple source 
*
*		@author Li yuchen
*
*		@date 1/25/2012
*/

#include "GenerateOptimizedSimple.h"
#include "PKB.h"
#include <fstream>
#include <stack>
#include <sstream>
using namespace std;

static void WriteProcedure(stringstream& ss, TNode* proc_tnode);
static void WriteStmtList(stringstream& ss, TNode* stmtlist_tnode, int indent_level);
static void WriteAssign(stringstream& ss, TNode* assign_tnode);
static void WriteCall(stringstream& ss, TNode* call_tnode);
static void WriteWhile(stringstream& ss, TNode* while_tnode, int indent_level);
static void WriteIf(stringstream& ss, TNode* if_tnode, int indent_level);
static void WriteExpr(stringstream& ss, TNode* expr_tnode);
static void WriteIndent(stringstream& ss, int indent_level);

void WriteIndent(stringstream& ss, int indent_level){
	for(int i = 0; i < indent_level; i++){
		ss << "	";
	}
}

void WriteExpr(stringstream& ss, TNode* expr_tnode){
	if(expr_tnode->child == NULL){
		switch(expr_tnode->type){
		case VAR_TYPE:
			ss << VarTable::GetVarName(expr_tnode->value);
			break;
		case CONST_TYPE:
			ss << expr_tnode->value;
			break;
		}
		return;
	}
	if(expr_tnode->child->child != NULL){
		ss << "(";
	}
	WriteExpr(ss,expr_tnode->child);
	if(expr_tnode->child->child != NULL){
		ss << ")";
	}
	char op = expr_tnode->value;
	ss << " " << op << " ";
	if(expr_tnode->child->right_sibling->child != NULL){
		ss << "(";
	}
	WriteExpr(ss,expr_tnode->child->right_sibling);
	if(expr_tnode->child->right_sibling->child != NULL){
		ss << ")";
	}
	
}

void WriteWhile(stringstream& ss, TNode* while_tnode, int indent_level){
	WriteIndent(ss,indent_level); 
	ss << "while " << VarTable::GetVarName(while_tnode->child->value) << " {" << endl;
	WriteStmtList(ss,while_tnode->child->right_sibling,(indent_level+1));
	WriteIndent(ss,indent_level); 
	ss << "}" << endl;
}

void WriteIf(stringstream& ss, TNode* if_tnode, int indent_level){
	WriteIndent(ss,indent_level); 
	ss << "if " << VarTable::GetVarName(if_tnode->child->value) << " then {" << endl;
	WriteStmtList(ss,if_tnode->child->right_sibling, (indent_level+1));
	WriteIndent(ss,indent_level); 
	ss << "}else{" << endl;
	WriteStmtList(ss,if_tnode->child->right_sibling->right_sibling,(indent_level+1));
	WriteIndent(ss,indent_level);
	ss << "}" << endl;
}

void WriteCall(stringstream& ss, TNode* call_tnode){
	ss << "call " << ProcTable::GetProcName(call_tnode->value) << ";" << endl;
}

void WriteAssign(stringstream& ss, TNode* assign_tnode){
	ss << VarTable::GetVarName(assign_tnode->child->value);
	ss << " = ";
	WriteExpr(ss,assign_tnode->child->right_sibling);
	ss << ";" << endl;
}

void WriteStmtList(stringstream& ss, TNode* stmtlist_tnode, int indent_level){
	TNode* traverser = stmtlist_tnode->child;
	string test_string;
	while(traverser != NULL){
		switch(traverser->type){
		case ASSIGN_TYPE:
			WriteIndent(ss,indent_level);
			WriteAssign(ss,traverser);
			break;
		case WHILE_TYPE:
			WriteWhile(ss,traverser,indent_level);
			break;
		case IF_TYPE:
			WriteIf(ss,traverser,indent_level);
			break;
		case CALL_TYPE:
			WriteIndent(ss,indent_level);
			WriteCall(ss,traverser);
			break;
		}
		test_string = ss.str();
		traverser = traverser->right_sibling;
	}
}

void WriteProcedure(stringstream& ss, TNode* proc_tnode){
	ss << "procedure " << ProcTable::GetProcName(proc_tnode->value)<< "{" << endl; 
	TNode* stmtlist_tnode = proc_tnode->child;
	/** DEBUG **/ if(stmtlist_tnode == NULL){ ss << "}" << endl; return; }
	WriteStmtList(ss,stmtlist_tnode,1);
	ss << "}" << endl;
}

void GenerateOptimizedSimple(TNode* AST_root,string file_path){
	ofstream file;
	file.open(file_path,ios::out);	
	stringstream output_file;

	TNode* traverser = AST_root->child;
	while(traverser != NULL){
		WriteProcedure(output_file,traverser);
		traverser = traverser->right_sibling;
	}
	file << output_file.str() << endl;
}